﻿// Copyright (c) BeiYinZhiNian (1031622947@qq.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: http://www.caviar.wang/ or https://gitee.com/Cherryblossoms/caviar.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Shapes;
using Core;
using Newtonsoft.Json;

namespace TemplateGeneratorApp.Core
{
    public class ProjectManager
    {
        private static string[] _ignore = { "/bin/", "/obj/" };

        public const string Version = "1.0.3";

        private static List<string> _packages = new List<string>()
        {
            $"/src/Caviar.Core/bin/Release/Caviar.Core.{Version}.nupkg",
            $"/src/Caviar.Infrastructure/bin/Release/Caviar.Infrastructure.{Version}.nupkg",
            $"/src/Caviar.SharedKernel/bin/Release/Caviar.SharedKernel.{Version}.nupkg",
        };

        private static List<string> _codeFileExtensions = new List<string>()
        {
            ".cs",".razor",".cshtml"
        };

        public static void CopyProject(ProjectInfo info)
        {
            using StreamReader sr = new StreamReader("./appsetting.json");
            string configStr = sr.ReadToEnd();
            var appSetting = JsonConvert.DeserializeObject<AppSetting>(configStr);

            var slnBuilder = new SlnBuilder();

            string destPackagePath = SafePath.Combine(info.Dest, "Packages");

            if (!Directory.Exists(destPackagePath))
                Directory.CreateDirectory(destPackagePath);

            foreach(var package in _packages)
            {

                string path = SafePath.Combine(appSetting!.CaviarBasePath, package);

                if (!File.Exists(path))
                    throw new FileNotFoundException($"所需的文件{path}不存在");

                File.Copy(path, SafePath.Combine(destPackagePath, new FileInfo(path).Name), true);

            }

            string uiCsproj = "";
            string uiRelativePath = "";

            switch (info.UIFramework)
            {
                case UIFramework.AntDesign:
                    {
                        string uipath = "/src/Caviar.AntDesignUI";

                        CopyFolderToDest(uipath, info.Dest, "Caviar.AntDesignUI", appSetting!.CaviarBasePath);

                        uiCsproj = SafePath.Combine(info.Dest, "Caviar.AntDesignUI", "Caviar.AntDesignUI.csproj");
                        uiRelativePath = "..\\Caviar.AntDesignUI\\Caviar.AntDesignUI.csproj";
                        string projTxt = "";
                        using (var projstream = File.OpenText(uiCsproj))
                        {
                            projTxt = projstream.ReadToEnd();
                            projTxt = projTxt.Replace("<ProjectReference Include=\"..\\Caviar.SharedKernel\\Caviar.SharedKernel.csproj\" />"
                                , $"<PackageReference Include=\"Caviar.SharedKernel\" Version=\"{Version}\" />\r\n");
                        }

                        using (StreamWriter sw = new StreamWriter(uiCsproj))
                        {
                            sw.Write(projTxt);
                        }

                        slnBuilder.AddProject("Caviar.AntDesignUI", "Caviar.AntDesignUI\\Caviar.AntDesignUI.csproj");

                        break;
                    }
                case UIFramework.TDesgin:
                    break;
                case UIFramework.MasaBlazor:
                    break;
                case UIFramework.BootStrapBlazor:
                    break;
                default:
                    {
                        throw new ArgumentOutOfRangeException("意外的UI框架");
                    }
            }

            switch (info.BlazorMode)
            {
                case BlazorMode.Hybrid:
                    {
                        string wasmProj = "/demo/hybrid/Caviar.Demo.Wasm/";
                        string hybridProj = "/demo/hybrid/Caviar.Demo.Hybrid/";

                        CopyFolderToDest(wasmProj, info.Dest, $"{info.Name}.Wasm", appSetting!.CaviarBasePath);
                        CopyFolderToDest(hybridProj, info.Dest, $"{info.Name}.Hybrid", appSetting!.CaviarBasePath);

                        string destWasmProj = SafePath.Combine(info.Dest, $"{info.Name}.Wasm", $"{info.Name}.Wasm.csproj");
                        string destHybridProj = SafePath.Combine(info.Dest, $"{info.Name}.Hybrid", $"{info.Name}.Hybrid.csproj");

                        File.Move(SafePath.Combine(info.Dest, $"{info.Name}.Wasm", $"Caviar.Demo.Wasm.csproj"), destWasmProj);
                        File.Move(SafePath.Combine(info.Dest, $"{info.Name}.Hybrid", $"Caviar.Demo.Hybrid.csproj"), destHybridProj);

                        var tempStr = "";

                        using (StreamReader wasmsr = new StreamReader(destWasmProj))
                        {
                            var str = wasmsr.ReadToEnd();
                            tempStr = str.Replace("<ProjectReference Include=\"..\\..\\..\\src\\Caviar.AntDesignUI\\Caviar.AntDesignUI.csproj\" />", $"<ProjectReference Include=\"{uiRelativePath}\" />")
                                .Replace("<ProjectReference Include=\"..\\..\\..\\src\\Caviar.SharedKernel\\Caviar.SharedKernel.csproj\" />", $"<PackageReference Include=\"Caviar.SharedKernel\" Version=\"{Version}\" />\r\n")
                                ;
                        }

                        using (StreamWriter wasmsw = new StreamWriter(destWasmProj))
                        {
                            wasmsw.Write(tempStr);
                        }

                        using (StreamReader hybridsr = new StreamReader(destHybridProj))
                        {
                            var str = hybridsr.ReadToEnd();
                            tempStr = str.Replace("<ProjectReference Include=\"..\\Caviar.Demo.Wasm\\Caviar.Demo.Wasm.csproj\" />", $"<ProjectReference Include=\"..\\{info.Name}.Wasm\\{info.Name}.Wasm.csproj\" />")
                                .Replace("<ProjectReference Include=\"..\\..\\..\\src\\Caviar.Infrastructure\\Caviar.Infrastructure.csproj\" />", $"<PackageReference Include=\"Caviar.Infrastructure\" Version=\"{Version}\" />\r\n")
                                ;
                        }

                        using (StreamWriter hybridsw = new StreamWriter(destHybridProj))
                        {
                            hybridsw.Write(tempStr);
                        }

                        ReplaceNameSpace("Caviar.Demo", info.Name, SafePath.Combine(info.Dest, $"{info.Name}.Wasm"));
                        ReplaceNameSpace("Caviar.Demo", info.Name, SafePath.Combine(info.Dest, $"{info.Name}.Hybrid"));

                        slnBuilder.AddProject(info.Name, $"{info.Name}.Wasm\\{info.Name}.Wasm.csproj");
                        slnBuilder.InsertProject(info.Name, $"{info.Name}.Hybrid\\{info.Name}.Hybrid.csproj",0);

                        break;
                    }
                case BlazorMode.Wasm:
                    {
                        string serverProj = "/demo/hybrid/Caviar.Demo.Server/";
                        string wasmProj = "/demo/hybrid/Caviar.Demo.Wasm/";

                        CopyFolderToDest(wasmProj, info.Dest, $"{info.Name}.Wasm", appSetting!.CaviarBasePath);
                        CopyFolderToDest(serverProj, info.Dest, $"{info.Name}.Server", appSetting!.CaviarBasePath);

                        string destWasmProj = SafePath.Combine(info.Dest, $"{info.Name}.Wasm", $"{info.Name}.Wasm.csproj");
                        string destServerProj = SafePath.Combine(info.Dest, $"{info.Name}.Server", $"{info.Name}.Server.csproj");

                        File.Move(SafePath.Combine(info.Dest, $"{info.Name}.Wasm", $"Caviar.Demo.Wasm.csproj"), destWasmProj);
                        File.Move(SafePath.Combine(info.Dest, $"{info.Name}.Server", $"Caviar.Demo.Server.csproj"), destServerProj);

                        break;
                    }
                default:
                    {
                        throw new ArgumentOutOfRangeException("意外的模式");
                    }
            }


            string slnStr = slnBuilder.BuildProjectString();
            using (StreamWriter sw = new StreamWriter(SafePath.Combine(info.Dest, $"{info.Name}.sln")))
            {
                sw.Write(slnStr);
            }
        }

        /// <summary>
        /// 复制文件夹到目标
        /// </summary>
        /// <param name="source">原始相对路径</param>
        /// <param name="dest">目标根目录</param>
        /// <param name="dir">目标文件夹名</param>
        /// <param name="caviarBasePath">项目根路径</param>
        private static void CopyFolderToDest(string source, string dest, string dir, string caviarBasePath)
        {
            string spath = source;
            var projectDir = SafePath.Combine(caviarBasePath, spath);
            string[] files = Directory.GetFiles(projectDir, "*.*", SearchOption.AllDirectories);

            foreach (var file in files)
            {
                string path = file.NormalizeDirStr();

                if (_ignore.Where(x => path.Contains(x)).Any())
                    continue;

                string dst = SafePath.Combine(dest, dir, file.Substring(file.IndexOf(spath) + spath.Length));

                var dstInfo = new FileInfo(dst);

                if (dstInfo.Directory is not null)
                    if (!Directory.Exists(dstInfo.Directory.FullName))
                        Directory.CreateDirectory(dstInfo.Directory.FullName);

                File.Copy(file, dst, true);
            }
        }

        /// <summary>
        /// 批量替换命名空间
        /// </summary>
        /// <param name="source">替换前</param>
        /// <param name="target">替换后</param>
        /// <param name="dir">目录</param>
        private static void ReplaceNameSpace(string source, string target, string dir)
        {
            string[] files = Directory.GetFiles(dir, "*.*", SearchOption.AllDirectories);
            foreach (var file in files)
            {
                var info = new FileInfo(file);

                if (_codeFileExtensions.Contains(info.Extension))
                {
                    var str = File.ReadAllText(file);
                    File.WriteAllText(file, str.Replace(source, target));
                }
            }

        }

        private static Encoding GetEncoding(string file)
        {
            using (FileStream fs = File.OpenRead(file))
            {
                Ude.CharsetDetector cdet = new Ude.CharsetDetector();
                cdet.Feed(fs);
                cdet.DataEnd();
                if (cdet.Charset != null)
                {
                    return Encoding.GetEncoding(cdet.Charset);
                }
                else
                {
                    return Encoding.UTF8;
                }
            }
        }
    }

    public class ProjectInfo
    {
        public string Name { get; set; }

        public UIFramework UIFramework { get; set; }

        public BlazorMode BlazorMode { get; set; }

        public string Dest { get; set; }


    }
}
